package top.yehot.kettle.boot.service.impl;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

import org.pentaho.di.core.database.DatabaseMeta;
import org.pentaho.di.core.encryption.Encr;
import org.pentaho.di.repository.AbstractRepository;
import org.pentaho.di.repository.RepositoryMeta;
import org.pentaho.di.repository.filerep.KettleFileRepository;
import org.pentaho.di.repository.kdr.KettleDatabaseRepository;
import org.pentaho.di.shared.SharedObjectInterface;
import org.pentaho.di.shared.SharedObjects;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;

import top.yehot.cmm.util.core.base.BaseException;
import top.yehot.cmm.util.core.utils.StrUtil;
import top.yehot.cmm.util.mybatis.utils.PageUtil;
import top.yehot.cmm.util.mybatis.utils.SpecialCharacterHandle;
import top.yehot.kettle.boot.core.SysConst;
import top.yehot.kettle.boot.core.utils.RepositoryUtil;
import top.yehot.kettle.boot.enums.DeleteEnum;
import top.yehot.kettle.boot.enums.KettleResEnum;
import top.yehot.kettle.boot.models.entity.Repository;
import top.yehot.kettle.boot.models.vo.repository.DatabaseInfoVO;
import top.yehot.kettle.boot.models.vo.repository.RepoAddVO;
import top.yehot.kettle.boot.models.vo.repository.RepoEditVO;
import top.yehot.kettle.boot.models.vo.repository.RepoSearchVO;
import top.yehot.kettle.boot.service.RepositoryService;
import top.yehot.kettle.boot.service.abstracts.AbstractRepositoryService;

/**
 * 资源库 服务实现类
 * 
 * @author lquan
 * @date 2022年5月28日 下午6:52:35
 */
@Service
public class RepositoryServiceImpl extends AbstractRepositoryService implements RepositoryService {

	@Override
	public IPage<Repository> list(RepoSearchVO param) {
		// 查询条件
		LambdaQueryWrapper<Repository> query = Wrappers.lambdaQuery(Repository.class);
		query.eq(Repository::getDelFlag, DeleteEnum.no);
		if (StringUtils.hasText(param.getRepName())) {
			query.like(Repository::getRepName, SpecialCharacterHandle.handleKeyword(param.getRepName()));
		}
		if (null != param.getRepType()) {
			query.eq(Repository::getRepType, param.getRepType());
		}
		query.orderByDesc(Repository::getCreateTime);
		// 查询
		return baseMapper.selectPage(PageUtil.of(param), query);
	}

	@Override
	public void testConnection(String id, RepoAddVO param) {
		// 拷贝对象
		Repository newRepo = packageRepository(param);
		// 设置ID
		newRepo.setId(id);

		// 连接资源库
		AbstractRepository abstractRepository = RepositoryUtil.createConnection(newRepo);
		// 判断是否链接成功
		if (abstractRepository == null || !abstractRepository.isConnected()) {
			throw new BaseException(KettleResEnum.CONNECTION_REPOSITORY_ERR);
		}
	}

	@Transactional
	@Override
	public void add(RepoAddVO param) {
		// 拷贝对象
		Repository newRepo = packageRepository(param);
		// 设置ID
		newRepo.setId(StrUtil.uuid());

		// 保存
		baseMapper.insert(newRepo);

		// 测试资源库是否配置正确
		testConnection(newRepo.getId(), param);

		// 更新 repositories.xml
		RepositoryUtil.writeRepositoryXml(newRepo.getId(), (ar, rm) -> {});
	}

	@Override
	public RepoEditVO getCusById(String id) {
		Repository repo = baseMapper.selectOne(Wrappers.lambdaQuery(Repository.class).eq(Repository::getId, id).eq(Repository::getDelFlag, DeleteEnum.no));
		Optional.ofNullable(repo).orElseThrow(() -> new BaseException(KettleResEnum.REPOSITORY_NOT_EXIST));
		
		// 拷贝对象
		RepoEditVO vo = new RepoEditVO();
		BeanUtils.copyProperties(repo, vo);
		vo.setRepType(repo.getRepType().name());
		return vo;
	}

	@Transactional
	@Override
	public void update(RepoEditVO param) {
		// 资源库ID
		String repId = param.getId();
		// 资源库名称(旧)
		String oldRepName = getCusById(repId).getRepName();
		
		// 拷贝对象
		Repository newRepo = packageRepository(param);
		
		// 修改
		baseMapper.updateById(newRepo);
		
		// 测试资源库是否配置正确
		testConnection(repId, param);
		
		// 更新 repositories.xml
		RepositoryUtil.writeRepositoryXml(repId, (ar, rm) -> {
			RepositoryMeta previousMeta = rm.searchRepository(oldRepName);
			if (null != previousMeta) {
				rm.removeRepository(rm.indexOfRepository(previousMeta));
			}
			DatabaseMeta dbPreviousMeta = rm.searchDatabase(oldRepName + SysConst.REP_DB_SUFFIX);
			if (null != dbPreviousMeta) {
				rm.removeDatabase(rm.indexOfDatabase(dbPreviousMeta));
			}
		});
	}

	@Transactional
	@Override
	public void deleteBatch(List<String> ids) {
		if (CollectionUtils.isEmpty(ids)) return;
		
		// 批量删除（逻辑删除）
		baseMapper.deleteByIds(DeleteEnum.yes.getValue(), ids.stream().distinct().collect(Collectors.toList()));
		
		for (String id : ids) {
			// 资源库名称(旧)
			String oldRepName = null;
			try {
				oldRepName = baseMapper.selectById(id).getRepName();
			} catch (Exception e) {
				throw new BaseException(KettleResEnum.REPOSITORY_NOT_EXIST);
			}
			// 删除对应节点
			RepositoryUtil.delRepositoryXml(oldRepName);
			// 关闭资源库连接
			Optional.ofNullable(RepositoryUtil.getRepositoryCache(id)).ifPresent(abstractRepository -> abstractRepository.disconnect());
			// 清除缓存
			RepositoryUtil.removeRepositoryCache(id);
		}
		
	}

	@Override
	public List<DatabaseInfoVO> getDatabasesByRepId(String repId) {
		AbstractRepository repository = RepositoryUtil.getRepository(repId);
		try {
			List<DatabaseMeta> databaseMetaList = null;
			if (repository instanceof KettleDatabaseRepository) {
				databaseMetaList = ((KettleDatabaseRepository) repository).getDatabases();
			}
			if (repository instanceof KettleFileRepository) {
				databaseMetaList = ((KettleFileRepository) repository).readDatabases();
			}
			if (null == databaseMetaList) {
				databaseMetaList = new ArrayList<DatabaseMeta>();
			}
			
			// 重新封装数据库连接信息
			List<DatabaseInfoVO> resultList = databaseMetaList.stream().map(db -> {
				return new DatabaseInfoVO()
						.setName(db.getName())
						.setType(db.getPluginId())
						.setHost(db.getHostname())
						.setPort(db.getDatabasePortNumberString())
						.setDatabaseName(db.getDatabaseName())
						.setUsername(db.getUsername())
						.setPassword(Encr.encryptPassword(db.getPassword()));
			}).collect(Collectors.toList());
			
			// 获取共享数据库
			SharedObjects so = new SharedObjects();
			for (SharedObjectInterface sharedDB : so.getObjectsMap().values()) {
				DatabaseMeta db = (DatabaseMeta) sharedDB;
				
				// 存在修改（数据库别名存在就修改为共享）
				DatabaseInfoVO existDb = resultList.stream().filter(rd -> rd.getName().equals(db.getName())).findFirst().orElse(null);
				if (null != existDb) {
					existDb.setShared(true);
					continue;
				}
				
				resultList.add(new DatabaseInfoVO()
						.setName(db.getName())
						.setType(db.getPluginId())
						.setHost(db.getHostname())
						.setPort(db.getDatabasePortNumberString())
						.setDatabaseName(db.getDatabaseName())
						.setUsername(db.getUsername())
						.setPassword(Encr.encryptPassword(db.getPassword()))
						.setShared(true)
					);
			}
			
			// 放入缓存中
			if (!CollectionUtils.isEmpty(databaseMetaList)) {
				SysConst.REP_DB_INFOS.put(repId, databaseMetaList);
			}
			return resultList;
		} catch (Exception e) {
			log.error(KettleResEnum.READ_REPOSITORY_DB_INFO_ERR.getMsg(), e);
		}
		return Collections.emptyList();
	}

	@Override
	public void sharedDb(String repId, String name) {
		List<DatabaseMeta> databaseMetaList = SysConst.REP_DB_INFOS.get(repId);
		Optional.ofNullable(databaseMetaList).orElseThrow(() -> new BaseException(KettleResEnum.REPO_LINK_DB_EMPTY));
		
		try {
			SharedObjects so = new SharedObjects();
			// 判断该数据库是否存在，存在删除
			DatabaseMeta dbMeta = so.getSharedDatabase(name);
			Optional.ofNullable(dbMeta).ifPresent(d -> so.removeObject(dbMeta));
			
			// 重新添加
			DatabaseMeta newDbMeta = databaseMetaList.stream().filter(d -> name.equals(d.getName())).findFirst().orElse(null);
			Optional.ofNullable(newDbMeta).ifPresent(d -> so.storeObject(d));
			
			// 写入文件 shared.xml
			so.saveToFile();
		} catch (Exception e) {
			log.error(KettleResEnum.SHARED_DB_ERR.getMsg(), e);
			throw new BaseException(KettleResEnum.SHARED_DB_ERR);
		}
	}

	@Override
	public void closeSharedDb(String name) {
		try {
			SharedObjects so = new SharedObjects();
			// 判断该数据库是否存在，存在删除
			DatabaseMeta dbMeta = so.getSharedDatabase(name);
			Optional.ofNullable(dbMeta).ifPresent(d -> so.removeObject(dbMeta));
			
			// 写入文件 shared.xml
			so.saveToFile();
		} catch (Exception e) {
			log.error(KettleResEnum.SHARED_DB_ERR.getMsg(), e);
			throw new BaseException(KettleResEnum.SHARED_DB_ERR);
		}
	}

}
